﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Microsoft.Win32;

namespace TestRunner 
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        private readonly LocalSettings settings = App.Settings;
        private readonly MainViewModel mainViewModel;
        private readonly OutputWriterStream outputStream;
        private readonly Action clearOutptuPane;
        private readonly Action<string> addStringAction;
        private readonly Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
        private bool removeStackTrace;
        public MainWindow()
        {
            InitializeComponent();
            addStringAction = AddLineToOutputOnUiThread;
            clearOutptuPane = ClearOutputOnUiThread;
            outputStream = new OutputWriterStream(AddLineToOutput, OnClearOutput);

            DataContext = mainViewModel = new MainViewModel(settings, outputStream);
            CommandBindings.AddRange(mainViewModel.CommandBindings);
            
            Closing += OnWindowClosing;
            Loaded += OnMainWindowLoaded;
            Icon = ToImageSource(Properties.Resources.AppIcon);

            Background = testTree.Background;
            testTree.SelectedItemChanged += testTree_SelectedItemChanged;

            mainViewModel.TestStarting += (s, e) => outputTextBlock.Clear();
            mainViewModel.ConfigureTools += OnToolsOption;
            mainViewModel.ShowCountDialog += mainViewModel_ShowCountDialog;
            CommandBinding openCommandBinding = new CommandBinding(ApplicationCommands.Open, OnOpenFile, mainViewModel.CanOpenCloseFile);
            CommandBindings.Add(openCommandBinding);

            InputBindings.Add(new KeyBinding(mainViewModel.RunTestsIndividuallyCommand, Key.R, ModifierKeys.Control | ModifierKeys.Shift));
            InputBindings.Add(new KeyBinding(mainViewModel.RunTestsWithParallelismCommand, Key.P, ModifierKeys.Control | ModifierKeys.Shift));
            InputBindings.Add(new KeyBinding(mainViewModel.RunTestsMulitpleTimesCommand, Key.M, ModifierKeys.Control | ModifierKeys.Shift));
            InputBindings.Add(new KeyBinding(mainViewModel.StopTestsCommand, Key.S, ModifierKeys.Control | ModifierKeys.Shift));
            InputBindings.Add(new KeyBinding(mainViewModel.ForceStopTestsCommand, Key.F, ModifierKeys.Control | ModifierKeys.Shift));
        }
        
        void mainViewModel_ShowCountDialog(object sender, ShowCountDialogEventArgs e) 
        {
            CountInputWindow dlg = new CountInputWindow();
            dlg.Line1Text = e.Line1Text;
            dlg.Line2Text = e.Line2Text;
            dlg.Title = e.Title;
            dlg.WindowStartupLocation = WindowStartupLocation.CenterOwner;
            dlg.Owner = this;
            dlg.Count = (uint) e.Count;
            var result = dlg.ShowDialog();
            if (result.HasValue && result.Value && dlg.Count > 0)
            {
                e.Count = (int)dlg.Count;
                e.Success = true;
            }
        }

        public static ImageSource ToImageSource(Icon icon)
        {
            ImageSource imageSource = Imaging.CreateBitmapSourceFromHIcon(
                icon.Handle,
                Int32Rect.Empty,
                BitmapSizeOptions.FromEmptyOptions());

            return imageSource;
        }
        void testTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            mainViewModel.SelectedItem = testTree.SelectedItem as ITestViewModel;
        }

        void OnMainWindowLoaded(object sender, RoutedEventArgs e) 
        {
            mainViewModel.LoadDefaultAsseblyList();
        }

        void OnWindowClosing(object sender, CancelEventArgs e) 
        {
            mainViewModel.UpdateSettings();
            settings.Save();
            Environment.Exit(0);
        }

        private void OnOpenFile(object sender, ExecutedRoutedEventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Filter = "Dynamic Link Libraries|*.dll|Executable|*.exe";
            dlg.Title = "Select the file containing the tests";
            dlg.Multiselect = true;
            var result = dlg.ShowDialog();
            if (result.Value)
            {
                //OnCloseFiles(sender, e);
                foreach (var fileName in dlg.FileNames.OrderBy(p=>p))
                {
                    mainViewModel.LoadTests(fileName);    
                }
            }
        }

        private void OnExit(object sender, RoutedEventArgs e)
        {
            Close();
        }
        
        private void Hyperlink_OnClick(object sender, RoutedEventArgs e)
        {
            var textBlock = sender as TextBlock;
            if (textBlock == null) return;
            string path = textBlock.ToolTip as string;
            if (String.IsNullOrWhiteSpace(path)) return;
            if (!String.IsNullOrWhiteSpace(path) && File.Exists(path)) 
            {
                try 
                {
                    Process.Start(path);
                } 
                catch (Exception ex) 
                {
                    Trace.WriteLine(ex.Message);
                }
            }
        }

        void OnToolsOption(object sender, EventArgs e) 
        {
            SettingsWindow window = new SettingsWindow();
            window.Owner = this;
            window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
            window.ShowDialog();
        }

        private void OnExpandAll(object sender, RoutedEventArgs e)
        {
            foreach (object item in testTree.Items)
            {
                TreeViewItem treeItem = testTree.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
                if (treeItem != null)
                {
                    ExpandCollapse(treeItem, true);
                    treeItem.IsExpanded = true;
                }
            }
        }

        private void OnCollapseAll(object sender, RoutedEventArgs e)
        {
            foreach (object item in testTree.Items)
            {
                TreeViewItem treeItem = testTree.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
                if (treeItem != null)
                {
                    ExpandCollapse(treeItem, false);
                    treeItem.IsExpanded = false;
                }
            }
        }

        void ExpandCollapse(ItemsControl items, bool expanded)
        {
            foreach (object obj in items.Items)
            {
                ItemsControl childControl = items.ItemContainerGenerator.ContainerFromItem(obj) as ItemsControl;
                if (childControl != null)
                {
                    ExpandCollapse(childControl, expanded);
                }
                TreeViewItem item = childControl as TreeViewItem;
                if (item != null)
                    item.IsExpanded = true;
            }
        }

        #region code to handle output pane
        void OnClearOutput()
        {
            dispatcher.Invoke(clearOutptuPane);
        }

        private const string StackTracePrefix = "    at ";
        void AddLineToOutput(string line)
        {
            if (String.IsNullOrWhiteSpace(line)) return;
            if (removeStackTrace && line.StartsWith(StackTracePrefix)) return;
            dispatcher.Invoke(addStringAction, line);
        }

        void ClearOutputOnUiThread()
        {
            outputTextBlock.Clear();
        }

        void AddLineToOutputOnUiThread(string line)
        {
            outputTextBlock.AppendText(Environment.NewLine);
            outputTextBlock.AppendText(line);

            if (autoScroolCheckBox.IsChecked.HasValue && autoScroolCheckBox.IsChecked.Value)
            {
                //outputTextBlock.SelectionStart = outputTextBlock.Text.Length;
                outputTextBlock.ScrollToEnd();
            }
        }

        private void OnRemoveStackTrace(object sender, RoutedEventArgs e)
        {
            removeStackTrace = removeStackTraceCheckBox.IsChecked.HasValue && removeStackTraceCheckBox.IsChecked.Value;
        }
        #endregion

        private void OnAbout(object sender, RoutedEventArgs e)
        {
            AboutBox1 dlg = new AboutBox1();
            dlg.ShowDialog();
        }

        private void OnClearOutputPane(object sender, RoutedEventArgs e)
        {
            outputTextBlock.Clear();
        }
    }
}

